﻿using System;
using System.Collections.Generic;
using System.Linq;
using Algoritmia.Utils;

namespace Algoritmia.DataStructures.Sets
{
    public interface ISet<T> : 
        IEnumerable<T>, 
        IEquatable<ISet<T>>,
        ICountable
    {
        void Add(T elem);
        void AddUnchecked(T elem);
        void Clear();
        bool Contains(T elem);
        void Discard(T elem);
        bool Remove(T elem);
    }

    public static class SetExtensions
    {
        public static bool IsDisjoint<T>(this ISet<T> self, ISet<T> other)
        {
            return self.All(elem => !other.Contains(elem));
        }

        public static ISet<T> Union<T>(this ISet<T> self, IEnumerable<T> other, Func<IEnumerable<T>,ISet<T>> createSet=null)
        {
            createSet = createSet ?? (items => AlgoritmiaFactory.Sets<T>.BuildDefault(items));
            return createSet(other.Union(self));
        }


        public static ISet<T> Intersect<T>(this ISet<T> self, IEnumerable<T> other, Func<IEnumerable<T>, ISet<T>> createSet = null)
        {
            createSet = createSet ?? (items => AlgoritmiaFactory.Sets<T>.BuildDefault(items));
            return createSet(from e in other
                             where self.Contains(e)
                             select e);
        }

        public static ISet<T> Substraction<T>(this ISet<T> self, IEnumerable<T> other, Func<IEnumerable<T>, ISet<T>> createSet = null)
        {
            createSet = createSet ?? (items => AlgoritmiaFactory.Sets<T>.BuildDefault(items));
            ISet<T> otherSet;
            if (other is ISet<T>)
            {
                otherSet = (ISet<T>)other;
            }
            else 
            {
                otherSet = createSet(other);            
            }
            return createSet(from e in self
                             where !otherSet.Contains(e)
                             select e);
        }

        public static ISet<T> Xor<T>(this ISet<T> self, IEnumerable<T> other, Func <IEnumerable<T>, ISet<T>> createSet = null)
        {
            createSet = createSet ?? (items => AlgoritmiaFactory.Sets<T>.BuildDefault(items));
            ISet<T> otherSet = other is ISet<T> ? (ISet<T>)other : createSet(other);

            ISet<T> firstSubstraction = self.Substraction(otherSet);
            ISet<T> secondSubstraction = otherSet.Substraction(self);

            return createSet(firstSubstraction.Union(secondSubstraction));
        }

        public static bool IsSubSetOf<T>(this ISet<T> self, ISet<T> other)
        {
            return other.IsSuperSetOf(self);
        }


        public static bool IsSuperSetOf<T>(this ISet<T> self, ISet<T> other)
        {   
            return self.Count >= other.Count && other.All(self.Contains);
        }


        public static bool IsProperSubSetOf<T>(this ISet<T> self, ISet<T> other)
        {
            return other.IsProperSuperSetOf(self);
        }


        public static bool IsProperSuperSetOf<T>(this ISet<T> self, ISet<T> other)
        {
            return self.Count > other.Count && other.All(self.Contains);
        }

    }
}
